package com.da.core.app.impl;

import com.da.core.app.BaseApp;
import com.da.core.context.Context;
import com.da.core.context.Session;
import com.da.core.context.impl.NIOContext;
import com.da.core.middleware.ErrorMiddleware;
import com.da.core.middleware.FlushMiddleware;
import com.da.core.util.Utils;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Set;

/**
 * @author da
 * @time 2023/9/16 上午 10:50
 */
public class NIOApp implements BaseApp {
    private Session session;

    public NIOApp() {
//        注册中间件
        middlewares.add(new FlushMiddleware());
        middlewares.add(new ErrorMiddleware());
    }

    @Override
    public Session getSession() {
        return this.session;
    }


    @SuppressWarnings("all")
    @Override
    public void start(int port) {
        try (Selector selector = Selector.open();  // 创建一个选择器
             ServerSocketChannel serverChannel = ServerSocketChannel.open()) {  // 创建服务器套接字通道
            serverChannel.configureBlocking(false);  // 设置为非阻塞模式
            serverChannel.socket().bind(new InetSocketAddress(port));  // 绑定端口
            serverChannel.register(selector, SelectionKey.OP_ACCEPT);  // 注册接受连接的事件类型

            Utils.printStartInfo("NIO", port);  // 打印启动信息

            while (true) {  // 循环等待事件发生
                if (selector.select() > 0) {  // 通过选择器等待事件发生
                    Set<SelectionKey> selectedKeys = selector.selectedKeys();  // 获取已就绪的事件集合
                    for (SelectionKey key : selectedKeys) {  // 遍历处理每个就绪事件
                        if (!key.isValid()) {  // 检查事件是否有效
                            continue;
                        }
                        if (key.isAcceptable()) {  // 对于接受连接的事件
                            SocketChannel accept = serverChannel.accept();  // 接受连接
                            accept.configureBlocking(false);  // 设置为非阻塞模式
                            accept.register(selector, SelectionKey.OP_READ);  // 注册读取数据的事件类型
                        } else if (key.isReadable()) {  // 对于可读取数据的事件
                            handleNioRequest(key);  // 处理读取请求
                        }
                    }
                    selectedKeys.clear();  // 清空已处理的事件集合
                }
            }
        } catch (java.net.BindException e) {  // 如果端口已被占用
            System.err.println("[NIO]错误提示: 端口 " + port + " 已经被占用了");
            throw new RuntimeException(e);
        } catch (Exception e) {  // 捕获其他异常
            throw new RuntimeException(e);
        }
    }

    @Override
    public void setSession(Session session) {
        this.session = session;
    }

    //        处理Nio请求
    private void handleNioRequest(SelectionKey key) throws IOException {
        SocketChannel channel = (SocketChannel) key.channel();  // 获取事件对应的套接字通道
        channel.configureBlocking(false);  // 设置非阻塞模式
        Context ctx = new NIOContext(channel, this);  // 创建上下文对象，用于处理请求
        // 打印接收到的请求信息
        if (null != ctx.getMethod() && null != ctx.getUrl()) {
            System.out.println("[NIO] 收到 [" + ctx.getMethod() + " " + ctx.getUrl() + "] 的请求");
        }
        handleRequest(ctx);  // 处理请求
    }
}
